package com.oj.controller;

import com.github.pagehelper.Page;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import com.oj.entity.*;
import com.oj.mapper.MessageMapper;
import com.oj.mapper.TagsMapper;
import com.oj.mapper.TagsviewMapper;
import com.oj.service.MessageService;
import com.oj.service.RatingService;
import com.oj.service.UserService;
import com.oj.util.*;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.servlet.ModelAndView;

import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.sql.Timestamp;
import java.text.SimpleDateFormat;
import java.util.*;

import static com.oj.util.CodeMsg.*;

@Slf4j
@Controller
@RequestMapping
public class MessageController {
    public static final String TOPIC_VIEW_COOKIE_PREFIX = "cookieOJTopicView";

    public static final int TOPIC_VIEW_COOKIE_EXPIRE = 24 * 3600;

    @Autowired
    private UserService userService;

    @Autowired
    private MessageMapper messageMapper;

    @Autowired
    private MessageService messageService;

    @Autowired
    private TagsMapper tagsMapper;

    @Autowired
    private TagsviewMapper tagsviewMapper;

    private void addTopicViewCookie(HttpServletRequest request, HttpServletResponse response, Integer topicId) {
        String ip = IpUtil.getIpAddr(request);
        Cookie cookie = new Cookie(TOPIC_VIEW_COOKIE_PREFIX + "_" + ip + "_" + topicId, "1");
        cookie.setMaxAge(TOPIC_VIEW_COOKIE_EXPIRE);
        cookie.setPath("/");
        response.addCookie(cookie);
    }

    private  boolean getTopicViewCookie(HttpServletRequest request, Integer topicId) {
        String ip = IpUtil.getIpAddr(request);
        Cookie cookies[] = request.getCookies();
        if(cookies != null){
            for(Cookie cookie : cookies){
                if(cookie.getName().equals(TOPIC_VIEW_COOKIE_PREFIX + "_" + ip + "_" + topicId)) {
                    return true;
                }
            }
        }
        return false;
    }

    @ResponseBody
    @RequestMapping(value = {"/api/topic/search"}, method = RequestMethod.GET)
    public Result<List> apiSearchTopics(String q, Integer limit, Integer offset) {
        if (limit == null) {
            limit = 0;
        }
        if (offset == null) {
            offset = 0;
        }
        List<Message> messages = messageService.search(q, limit, offset);
        for (Message message: messages) {
            if (message.getTitle().length() > 70) {
                message.setTitle(message.getTitle().subSequence(0, 70)+"...");
            }
            message.setUser(userService.query(message.getCreate_user()));
            message.setFriendly_Date(DateUtil.toFriendlyDate(message.getIn_date()));
            if (message.getViews()>=1000) {
                double num = message.getViews();
                message.setKviews(Math.floor(num / 1000 * 10) / 10 + "k");
            } else {
                message.setKviews(message.getViews().toString());
            }
            List<Message> replys = messageMapper.queryReplys(message.getMessage_id());
            if (replys != null && replys.size() > 0) {
                Message lastReply = replys.get(0);
                message.setLastRplUser(userService.query(lastReply.getCreate_user()));
                message.setLastReplyDate(lastReply.getIn_date());
                message.setLastReplyFriendlyDate(DateUtil.toFriendlyDate(lastReply.getIn_date()));
                message.setLastReplyId(lastReply.getMessage_id());
                message.setComments(replys.size());
            } else {
                message.setComments(0);
            }

            String bufString = new String();
            bufString = Html2Text.RemoveHtml(((message.getContent().length()>2000)?(message.getContent()).substring(0, 2000):message.getContent()));
            message.setContent_abstract(((bufString.length()>500)?(bufString).substring(0, 500)+"...":bufString));

            /* tags */
            List<Tags> tags = new ArrayList<>();
            for (Tagsview tagsview: tagsviewMapper.queryByMessages(message.getMessage_id())) {
                Tags t = tagsMapper.query(tagsview.getTag_id());
                tags.add(t);
            }
            message.setTags(tags);
        }
        return Result.success(messages);
    }
    @RequestMapping(value = {"/topics",
            "/topics/page/{pageNum}",
            "/topics/{username}/page/{pageNum}"}, method = RequestMethod.GET)
    public ModelAndView topics(@PathVariable(value = "pageNum",required = false)Integer pageNum,
                               @PathVariable(value = "username",required = false)String username,
                               @RequestParam(value = "pageSize",required = false, defaultValue = "20")Integer pageSize,
                               @RequestParam(value = "tag",required = false, defaultValue = "")String tag){
        if (pageNum == null) {
            pageNum = 1;
        }
        PageHelper.startPage(pageNum,pageSize);
        Map param = new HashMap();
        param.put("queryRootMessages", 1);
        param.put("username", username);
        if (tag.length() > 0) {
            Tags tags = tagsMapper.queryByName(tag);
            if (tags != null) {
                param.put("tagId", tags.getTag_id());
            }
        }
        Page<Message> messages = messageMapper.findByPaging(param);
        for (Message message: messages) {
            if (message.getTitle().length() > 70) {
                message.setTitle(message.getTitle().subSequence(0, 70)+"...");
            }
            message.setUser(userService.query(message.getCreate_user()));
            message.setFriendly_Date(DateUtil.toFriendlyDate(message.getIn_date()));
            if (message.getViews()>=1000) {
                double num = message.getViews();
                message.setKviews(Math.floor(num / 1000 * 10) / 10 + "k");
            } else {
                message.setKviews(message.getViews().toString());
            }
            List<Message> replys = messageMapper.queryReplys(message.getMessage_id());
            if (replys != null && replys.size() > 0) {
                Message lastReply = replys.get(0);
                message.setLastRplUser(userService.query(lastReply.getCreate_user()));
                message.setLastReplyDate(lastReply.getIn_date());
                message.setLastReplyFriendlyDate(DateUtil.toFriendlyDate(lastReply.getIn_date()));
                message.setLastReplyId(lastReply.getMessage_id());
                message.setComments(replys.size());
            } else {
                message.setComments(0);
            }

            String bufString = new String();
            bufString = Html2Text.RemoveHtml(((message.getContent().length()>2000)?(message.getContent()).substring(0, 2000):message.getContent()));
            message.setContent_abstract(((bufString.length()>500)?(bufString).substring(0, 500)+"...":bufString));

            /* tags */
            List<Tags> tags = new ArrayList<>();
            for (Tagsview tagsview: tagsviewMapper.queryByMessages(message.getMessage_id())) {
                Tags t = tagsMapper.query(tagsview.getTag_id());
                tags.add(t);
            }
            message.setTags(tags);
        }
        PageInfo<Message> pageInfo = new PageInfo<>(messages);
        ModelAndView mv = new ModelAndView();
        mv.addObject("pageInfo", pageInfo);
        mv.addObject("username", username);
        mv.setViewName("topic/topics.html");
        return mv;
    }

    @GetMapping("/topic/new")
    public ModelAndView topicNew(){
        try {
            Message message = new Message();
            if (message == null) {
                return null;
            }
            message.setProblem_id(0);
            message.setMessage_id(0);
            message.setParent_id(0);
            message.setRoot_id(0);
            ModelAndView mv=new ModelAndView();
            mv.addObject("message", message);
            mv.setViewName("topic/topic-edit.html");
            return mv;
        }catch (Exception e) {
        }
        return null;
    }

    @GetMapping("/topic/edit/{messageId}")
    public ModelAndView topicEdit(@PathVariable(value = "messageId",required = false)Integer messageId,
                                  HttpServletRequest request){
        try {
            String username = request.getSession().getAttribute("session_username").toString();
            if (username == null) {
                return null;
            }
            Message message = messageMapper.query(messageId);
            if (message == null || !message.getCreate_user().equals(username)) {
                return null;
            }
            String tagsList = new String();
            List<Tagsview> tagsview = tagsviewMapper.queryByMessages(messageId);
            for (Tagsview t:tagsview) {
                Tags tag = tagsMapper.query(t.getTag_id());
                if (tagsList.length() == 0) {
                    tagsList += tag.getName();
                } else {
                    tagsList += "," + tag.getName();
                }
            }
            message.setTag(tagsList);
            ModelAndView mv=new ModelAndView();
            mv.addObject("message", message);
            mv.setViewName("topic/topic-edit.html");
            return mv;
        }catch (Exception e) {
        }
        return null;
    }

    @GetMapping("/topic/{messageId}")
    public ModelAndView topic(@PathVariable(value = "messageId",required = false)Integer messageId,
                              HttpServletRequest request, HttpServletResponse response){
        Message message = messageMapper.query(messageId);
        if (message == null || !message.getMessage_id().equals(message.getRoot_id())) {
            return null;
        }
        message.setUser(userService.query(message.getCreate_user()));
        message.setFriendly_Date(DateUtil.toFriendlyDate(message.getIn_date()));
        if (message.getViews()>=1000) {
            double num = message.getViews();
            message.setKviews(Math.floor(num / 1000 * 10) / 10 + "k");
        } else {
            message.setKviews(message.getViews().toString());
        }
        List<Message> childs = messageMapper.queryChildReplys(message.getMessage_id());
        List<Message> replyQueue = new ArrayList<Message>();
        List<Message> replys = new ArrayList<Message>();

        replyQueue.add(message);
        while (replyQueue.size() != 0) {
            Integer size_ = replyQueue.size();
            Message reply = replyQueue.get(size_ - 1);
            replys.add(reply);
            List<Message> childs_ = messageMapper.queryChildReplys(reply.getMessage_id());
            if(childs_ != null) {
                reply.setComments(childs_.size());
            } else {
                reply.setComments(0);
            }
            replyQueue.remove(size_ - 1);
            if(childs_!=null){
                replyQueue.addAll(childs_);
            }
        }
        replys.remove(0);

        /* 评论总数 */
        if (replys != null && replys.size() > 0) {
            message.setComments(replys.size());
        } else {
            message.setComments(0);
        }

        /* 评论层级深度计算 */
        HashMap<Integer, Integer> depthMap = new HashMap<Integer, Integer>();
        Integer size = replys.size();
        for (Integer i = 0; i < size; i++) {
            Message comment = replys.get(i);
            if(comment.getParent_id().equals(comment.getRoot_id())){
                depthMap.put(comment.getMessage_id(), 0);
                comment.setCommentDepth(0);
            } else {
                Integer depth = depthMap.get(comment.getParent_id());
                if (depth != null) {
                    depthMap.put(comment.getMessage_id(), depth + 1);
                    comment.setCommentDepth(depth + 1);
                } else {
                    depthMap.put(comment.getMessage_id(), 0);
                    comment.setCommentDepth(0);
                }
            }

            /* 额外信息转换 */
            comment.setUser(userService.query(comment.getCreate_user()));
            comment.setFriendly_Date(DateUtil.toFriendlyDate(comment.getIn_date()));
        }

        /* 更新阅读计数 */
        if (getTopicViewCookie(request, message.getMessage_id()) == false) {
            addTopicViewCookie(request, response, message.getMessage_id());
            message.setViews(message.getViews() + 1);
            if (messageMapper.update(message)) {
            }
        }

        /* tags */
        String tag = new String();
        List<Tags> tags = new ArrayList<>();
        for (Tagsview tagsview: tagsviewMapper.queryByMessages(message.getMessage_id())) {
            Tags t = tagsMapper.query(tagsview.getTag_id());
            tags.add(t);
            if (tag.length() > 0) {
                tag += ", ";
            }
            tag += t.getName();
        }
        message.setTags(tags);
        message.setTag(tag);

        ModelAndView mv = new ModelAndView();
        mv.addObject("message", message);
        mv.addObject("comments", replys);
        mv.setViewName("topic/topic.html");
        return mv;
    }

    @ResponseBody
    @PostMapping(value = "/api/topic/post")
    public Result postMessage(@RequestParam Map<String, Object> map, HttpServletRequest request){
        if (request.getSession().getAttribute("session_username") == null) {
            return Result.error(CodeMsg.USER_NOT_LOGIN.locale());
        }
        if (map.get("type") == null) {
            return Result.error(CodeMsg.PARAM_INVALID.locale());
        }

        if (map.get("type").toString().equals("new")) {
            return newMessage(map, request);
        }

        if (map.get("type").toString().equals("edit")) {
            return editMessage(map, request);
        }

        if (map.get("type").toString().equals("delete")) {
            return deleteMessage(map, request);
        }

        return Result.success();
    }

    public Result newMessage(Map<String, Object> map, HttpServletRequest request) {
        Message message = new Message();
        try {
            Date dt = new Date();
            SimpleDateFormat simpleDate = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
            message.setIn_date(Timestamp.valueOf(simpleDate.format(dt)));

            if (map.get("parentId") != null) {
                message.setParent_id(Integer.parseInt(map.get("parentId").toString()));
            }

            if (map.get("problemId") != null) {
                message.setProblem_id(Integer.parseInt(map.get("problemId").toString()));
            }

            Message rootMessage = null;
            if (map.get("rootId") != null) {
                message.setRoot_id(Integer.parseInt(map.get("rootId").toString()));
                rootMessage = messageMapper.query(message.getRoot_id());
            }

            if (map.get("title") != null && map.get("title").toString().length() != 0) {
                message.setTitle(map.get("title").toString());
            } else {
                if (rootMessage == null) {
                    return Result.error(CodeMsg.INPUT_TITLE_NULL.locale());
                }
                /* 为空说明是评论,获取根贴标题 */
                message.setProblem_id(rootMessage.getProblem_id());
                message.setTitle(rootMessage.getTitle());
            }
            if (map.get("content").toString().length() == 0) {
                return Result.error(CodeMsg.INPUT_CONTENT_NULL.locale());
            }
            message.setContent(map.get("content").toString());

            if (map.get("tags") != null) {
                message.setTag(map.get("tags").toString());
            }

            message.setCreate_user(request.getSession().getAttribute("session_username").toString());

            if (messageMapper.insert(message)) {
                if(message.getRoot_id().equals(0)){
                    Message messageNew = new Message();
                    messageNew.setMessage_id(message.getMessage_id());
                    messageNew.setRoot_id(message.getMessage_id());
                    if (!messageMapper.update(messageNew)) {
                        Result.error(CodeMsg.INNER_FAULT.locale());
                    }
                    saveMessageTags(message.getMessage_id(), message.getTag(), request);
                    messageService.topTopicsGenerator();
                    return Result.success(message.getMessage_id());
                } else {
                    User user_ = userService.query(messageMapper.query(message.getParent_id()).getCreate_user());
                    MailUtil mailUtil = new MailUtil();
                    mailUtil.sendHtmlMail(user_.getEmail(), "New reply from " + message.getCreate_user(),
                            "happyoj.com/topic/" + message.getRoot_id() + "<br><b>Topic: " + message.getTitle() + "</b><br>" +
                                    message.getContent()
                    );
                }
                messageService.topTopicsGenerator();
                return Result.success(message.getRoot_id());
            }
        } catch (Exception e) {
            log.error("newMessage Exception."+ e.getMessage());
        }
        return Result.error(CodeMsg.INNER_FAULT.locale());
    }
    public Result editMessage(Map<String, Object> map, HttpServletRequest request) {
        Message message = new Message();
        try {
            if (map.get("content").toString().length() == 0 || map.get("messageId") == null) {
                return Result.error(CodeMsg.INPUT_CONTENT_NULL.locale());
            }

            message.setContent(map.get("content").toString());
            message.setMessage_id(Integer.parseInt(map.get("messageId").toString()));
            if (map.get("title") != null) {
                message.setTitle(map.get("title").toString());
            }

            if (map.get("tags") != null) {
                message.setTag(map.get("tags").toString());
            }

            String username = request.getSession().getAttribute("session_username").toString();
            if (username == null) {
                return Result.error(CodeMsg.USER_NOT_LOGIN.locale());
            }
            if (!messageMapper.query(message.getMessage_id()).getCreate_user().equals(username)) {
                return Result.error(CodeMsg.PARAM_INVALID.locale());
            }
            if (messageMapper.update(message)) {
                saveMessageTags(message.getMessage_id(), message.getTag(), request);
                messageService.topTopicsGenerator();
                return Result.success(message.getMessage_id());
            }
        } catch (Exception e) {
            log.error("saveMessage Exception."+ e.getMessage());
        }
        return Result.error(CodeMsg.INNER_FAULT.locale());
    }

    public Result deleteMessage(Map<String, Object> map, HttpServletRequest request) {
        try {
            Integer messageId = null;
            if (map.get("messageId") != null && !"".equals(map.get("messageId").toString())) {
                messageId = Integer.parseInt(map.get("messageId").toString());
            }
            if (messageId == null) {
                return Result.error(CodeMsg.PARAM_NULL.locale());
            }

            String username = request.getSession().getAttribute("session_username").toString();
            Message message = messageMapper.query(messageId);
            if (message == null || !message.getCreate_user().equals(username)) {
                return Result.error(CodeMsg.PARAM_INVALID.locale());
            }

            Message messageNew = new Message();
            messageNew.setMessage_id(messageId);
            messageNew.setDefunct("Y");
            if (messageMapper.update(messageNew)) {
                messageService.topTopicsGenerator();
                return Result.success();
            }
        } catch (Exception e) {
            log.error("delMessage Exception.");
        }
        return Result.error(CodeMsg.INNER_FAULT.locale());
    }
    public boolean saveMessageTags(Integer messageId, String tags, HttpServletRequest request) {
        tagsviewMapper.deleteByMessageId(messageId);
        String[] tagsString = tags.split(",");
        for(String t_:tagsString){
            t_ = t_.trim();
            if (t_.length() == 0) {
                continue;
            }
            Tags tag_ = tagsMapper.queryByName(t_);
            if(tag_ == null){
                tag_ = new Tags();
                tag_.setCreate_user(userService.getUserBySession(request).getUsername());
                tag_.setIndate(new Date());
                tag_.setName(t_);
                tagsMapper.insert(tag_);
            }
            Tagsview tagsview_ = new Tagsview();
            tagsview_.setTag_id(tag_.getTag_id());
            tagsview_.setMessage_id(messageId);
            tagsviewMapper.insert(tagsview_);
        }
        return true;
    }

    @ResponseBody
    @PostMapping(value = "/api/admin/topic/order")
    public Result topicOrderNum(@RequestParam Map<String, Object> map, HttpServletRequest request){
        log.info("/api/admin/topic/order: " + map.toString());
        if (map.get("topicId") == null || map.get("orderNum") == null) {
            return Result.error(CodeMsg.PARAM_INVALID.locale());
        }
        try {
            Integer messageId = Integer.parseInt(map.get("topicId").toString());
            Integer orderNum = Integer.parseInt(map.get("orderNum").toString());
            Message message = messageMapper.query(messageId);
            if (message == null) {
                return Result.error(CodeMsg.PARAM_INVALID.locale());
            }
            message.setOrderNum(orderNum);
            if (messageMapper.update(message)) {
            }
        } catch (Exception e) {
            return Result.error(CodeMsg.PARAM_INVALID.locale());
        }
        return Result.success();
    }

    @ResponseBody
    @RequestMapping(value = {"/api/problem/editorial/query"}, method = RequestMethod.GET)
    public Result<PageInfo<Message>> editorials(@RequestParam Map<String, Object> map, HttpServletRequest request){
        Integer problemId = 0;
        if (map.get("problemId") != null && !"".equals(map.get("problemId").toString())) {
            problemId = Integer.parseInt(map.get("problemId").toString());
        }
        Integer pageNum = 1;
        if (map.get("pageNum") != null && !"".equals(map.get("pageNum").toString())) {
            pageNum = Integer.parseInt(map.get("pageNum").toString());
        }
        String tag = "";
        if (map.get("tag") != null && !"".equals(map.get("tag").toString())) {
            tag = map.get("problemId").toString();
        }

        Integer pageSize = 50;
        PageHelper.startPage(pageNum, pageSize);
        Map param = new HashMap();
        param.put("queryRootMessages", 1);
        param.put("problemId", problemId);
        if (tag.length() > 0) {
            Tags tags = tagsMapper.queryByName(tag);
            if (tags != null) {
                param.put("tagId", tags.getTag_id());
            }
        }
        Page<Message> messages = messageMapper.findByPaging(param);
        for (Message message: messages) {
            if (message.getTitle().length() > 70) {
                message.setTitle(message.getTitle().subSequence(0, 70)+"...");
            }
            message.setUser(userService.query(message.getCreate_user()));
            message.setFriendly_Date(DateUtil.toFriendlyDate(message.getIn_date()));
            List<Message> replys = messageMapper.queryReplys(message.getMessage_id());
            if (replys != null && replys.size() > 0) {
                Message lastReply = replys.get(0);
                message.setLastRplUser(userService.query(lastReply.getCreate_user()));
                message.setLastReplyDate(lastReply.getIn_date());
                message.setLastReplyFriendlyDate(DateUtil.toFriendlyDate(lastReply.getIn_date()));
                message.setLastReplyId(lastReply.getMessage_id());
                message.setComments(replys.size());
            } else {
                message.setComments(0);
            }

            String bufString = new String();
            bufString = Html2Text.RemoveHtml(((message.getContent().length()>2000)?(message.getContent()).substring(0, 2000):message.getContent()));
            message.setContent_abstract(((bufString.length()>500)?(bufString).substring(0, 500)+"...":bufString));

            int newlineIndex = message.getContent_abstract().indexOf("\n");
            if (newlineIndex != -1) {
                message.setContent_abstract(message.getContent_abstract().substring(0, newlineIndex));
            }

            /* tags */
            List<Tags> tags = new ArrayList<>();
            for (Tagsview tagsview: tagsviewMapper.queryByMessages(message.getMessage_id())) {
                Tags t = tagsMapper.query(tagsview.getTag_id());
                tags.add(t);
            }
            message.setTags(tags);
        }
        PageInfo<Message> pageInfo = new PageInfo<>(messages);
        return Result.success(pageInfo);
    }
}